#ifndef U2Root_H
#define U2Root_H


#include "U2PreRequest.h"
#include "U2Singleton.h"
#include "U2MemoryAllocatorConfig.h"
#include "U2FrameListener.h"
#include "U2StringUtil.h"
#include "U2SceneManagerEnumerator.h"


U2EG_NAMESPACE_BEGIN


class U2IPlugin;
class U2DynLib;
class U2RenderSystem;
class U2Timer;
class U2LogManager;
class U2DynLibManager;
class U2ArchiveManager;
class U2ArchiveFactory;
class U2ResourceGroupManager;
class U2ResourceBackgroundQueue;
class WorkQueue;
class U2RenderWindow;
class U2RenderSystemCapabilities;
class U2RenderSystemCapabilitiesManager;
class U2SceneManager;
class U2SceneManagerEnumerator;
class ControllerManager;


typedef std::vector<U2RenderSystem*>    U2RenderSystemList;


class _U2Share U2Root : public U2Singleton<U2Root>, public RootAlloc
{
public:
	typedef std::vector<U2DynLib*>      PluginLibList;
	typedef std::vector<U2IPlugin*>     PluginInstanceList;

public:
    /** Constructor
    @param pluginFileName The file that contains plugins information.
        Defaults to "plugins.cfg", may be left blank to ignore.
	@param configFileName The file that contains the configuration to be loaded.
		Defaults to "ogre.cfg", may be left blank to load nothing.
	@param logFileName The logfile to create, defaults to Ogre.log, may be 
		left blank if you've already set up LogManager & Log yourself
	*/
    U2Root(const U2String& pluginFileName = "plugins.cfg", 
		const U2String& configFileName = "u2.cfg", 
		const U2String& logFileName = "u2.log");
	virtual ~U2Root(void);
	static U2Root&	getSingleton(void);
	static U2Root*	getSingletonPtr(void);

	void			loadPlugin(const U2String& pluginName);
	void			unloadPlugin(const U2String& pluginName);
	void			installPlugin(U2IPlugin* pPlugin);
	void			uninstallPlugin(U2IPlugin* pPlugin);

    /** Saves the details of the current configuration
    @remarks
        Stores details of the current configuration so it may be
        restored later on.
    */
    void saveConfig(void);

    /** Checks for saved video/sound/etc settings
    @remarks
        This method checks to see if there is a valid saved configuration
        from a previous run. If there is, the state of the system will
        be restored to that configuration.

    @returns
        If a valid configuration was found, <b>true</b> is returned.
    @par
        If there is no saved configuration, or if the system failed
        with the last config settings, <b>false</b> is returned.
    */
    bool restoreConfig(void);

    /** Adds a new rendering subsystem to the list of available renderers.
    @remarks
        Intended for use by advanced users and plugin writers only!
        Calling this method with a pointer to a valid U2RenderSystem
        (subclass) adds a rendering API implementation to the list of
        available ones. Typical examples would be an OpenGL
        implementation and a Direct3D implementation.
    @note
        <br>This should usually be called from the dllStartPlugin()
        function of an extension plug-in.
    */
    void addRenderSystem(U2RenderSystem* newRend);

    /** Retrieve a list of the available render systems.
    @remarks
        Retrieves a pointer to the list of available renderers as a
        list of U2RenderSystem subclasses. Can be used to build a
        custom settings dialog.
    */
    const U2RenderSystemList& getAvailableRenderers(void);

    /** Retrieve a pointer to the render system by the given name
    @param
        name Name of the render system intend to retrieve.
    @returns
        A pointer to the render system, <b>NULL</b> if no found.
    */
    U2RenderSystem* getRenderSystemByName(const U2String& name);

    /** Sets the rendering subsystem to be used.
    @remarks
        This method indicates to OGRE which rendering system is to be
        used (e.g. Direct3D, OpenGL etc). This is called
        automatically by the default config dialog, and when settings
        are restored from a previous configuraion. If used manually
        it could be used to set the renderer from a custom settings
        dialog. Once this has been done, the renderer can be
        initialised using Root::initialise.
    @par
        This method is also called by render systems if they are
        initialised directly.
    @param
        system Pointer to the render system to use.
    @see
        U2RenderSystem
    */
    void setRenderSystem(U2RenderSystem* system);

    /** Retrieve a pointer to the currently selected render system.
    */
    U2RenderSystem* getRenderSystem(void);

    /** Gets a pointer to the central timer used for all OGRE timings */
    U2Timer* getTimer(void);

    /** Registers a U2FrameListener which will be called back every frame.
    @remarks
        A U2FrameListener is a class which implements methods which
        will be called every frame.
    @par
        See the U2FrameListener class for more details on the specifics
        It is imperitive that the instance passed to this method is
        not destroyed before either the rendering loop ends, or the
        class is removed from the listening list using
        removeFrameListener.
    @note
        <br>This method can only be called after Root::initialise has
        been called.
    @see
        U2FrameListener, U2Root::removeFrameListener
    */
    void addFrameListener(U2FrameListener* newListener);

    /** Removes a U2FrameListener from the list of listening classes.
    @see
        U2FrameListener, U2Root::addFrameListener
    */
    void removeFrameListener(U2FrameListener* oldListener);

    /** Starts / restarts the automatic rendering cycle.
    @remarks
        This method begins the automatic rendering of the scene. It
        will <b>NOT</b> return until the rendering cycle is halted.
    @par
        During rendering, any FrameListener classes registered using
        addFrameListener will be called back for each frame that is
        to be rendered, These classes can tell OGRE to halt the
        rendering if required, which will cause this method to
        return.
    @note
        <br>Users of the OGRE library do not have to use this
        automatic rendering loop. It is there as a convenience and is
        most useful for high frame rate applications e.g. games. For
        applications that don't need to constantly refresh the
        rendering targets (e.g. an editor utility), it is better to
        manually refresh each render target only when required by
        calling RenderTarget::update, or if you want to run your own
		render loop you can update all targets on demand using
		Root::renderOneFrame.
    @note
        This frees up the CPU to do other things in between
        refreshes, since in this case frame rate is less important.
    @note
        This method can only be called after Root::initialise has
        been called.
    */
    void startRendering(void);

    /** Queues the end of rendering.
    @remarks
        This method will do nothing unless startRendering() has
        been called, in which case before the next frame is rendered
        the rendering loop will bail out.
    @see
        Root, Root::startRendering
    */
    void queueEndRendering(void);

    /** Render one frame. 
    @remarks
        Updates all the render targets automatically and then returns,
        raising frame events before and after.
    */
    bool renderOneFrame(void);

	/** Render one frame, with custom frame time information. 
	@remarks
	    Updates all the render targets automatically and then returns,
	    raising frame events before and after - all per-frame times are based on
	    the time value you pass in.
	*/
	bool renderOneFrame(u2real timeSinceLastFrame);

    /** Method for raising frame started events. 
    @remarks
        This method is only for internal use when you use OGRE's inbuilt rendering
        loop (Root::startRendering). However, if you run your own rendering loop then
        you should call this method to ensure that FrameListener objects are notified
        of frame events; processes like texture animation and particle systems rely on 
        this.
    @par
        Calling this method also increments the frame number, which is
        important for keeping some elements of the engine up to date.
    @note
        This method takes an event object as a parameter, so you can specify the times
        yourself. If you are happy for OGRE to automatically calculate the frame time
        for you, then call the other version of this method with no parameters.
    @param evt Event object which includes all the timing information which you have 
        calculated for yourself
    @returns False if one or more frame listeners elected that the rendering loop should
        be terminated, true otherwise.
    */
    bool _fireFrameStarted(FrameEvent& evt);
    /** Method for raising frame rendering queued events. 
    @remarks
        This method is only for internal use when you use OGRE's inbuilt rendering
        loop (Root::startRendering). However, if you run your own rendering loop then
        you should call this method too, to ensure that all state is updated
		correctly. You should call it after the windows have been updated
		but before the buffers are swapped, or if you are not separating the
		update and buffer swap, then after the update just before _fireFrameEnded.
    */
    bool _fireFrameRenderingQueued(FrameEvent& evt);

    /** Method for raising frame ended events. 
    @remarks
        This method is only for internal use when you use OGRE's inbuilt rendering
        loop (Root::startRendering). However, if you run your own rendering loop then
        you should call this method to ensure that FrameListener objects are notified
        of frame events; processes like texture animation and particle systems rely on 
        this.
    @note
        This method takes an event object as a parameter, so you can specify the times
        yourself. If you are happy for OGRE to automatically calculate the frame time
        for you, then call the other version of this method with no parameters.
    @param evt Event object which includes all the timing information which you have 
        calculated for yourself
    @returns False if one or more frame listeners elected that the rendering loop should
        be terminated, true otherwise.
    */
    bool _fireFrameEnded(FrameEvent& evt);
    /** Method for raising frame started events. 
    @remarks
        This method is only for internal use when you use OGRE's inbuilt rendering
        loop (Root::startRendering). However, if you run your own rendering loop then
        you should call this method to ensure that FrameListener objects are notified
        of frame events; processes like texture animation and particle systems rely on 
        this.
    @par
        Calling this method also increments the frame number, which is
        important for keeping some elements of the engine up to date.
    @note
        This method calculates the frame timing information for you based on the elapsed
        time. If you want to specify elapsed times yourself you should call the other 
        version of this method which takes event details as a parameter.
    @returns False if one or more frame listeners elected that the rendering loop should
        be terminated, true otherwise.
    */
    bool _fireFrameStarted();
    /** Method for raising frame rendering queued events. 
    @remarks
        This method is only for internal use when you use OGRE's inbuilt rendering
        loop (Root::startRendering). However, if you run your own rendering loop then
        you you may want to call this method too, although nothing in OGRE relies on this
		particular event. Really if you're running your own rendering loop at
		this level of detail then you can get the same effect as doing your
		updates in a frameRenderingQueued callback by just calling 
		U2RenderWindow::update with the 'swapBuffers' option set to false. 
    */
    bool _fireFrameRenderingQueued();
    /** Method for raising frame ended events. 
    @remarks
        This method is only for internal use when you use OGRE's inbuilt rendering
        loop (Root::startRendering). However, if you run your own rendering loop then
        you should call this method to ensure that FrameListener objects are notified
        of frame events; processes like texture animation and particle systems rely on 
        this.
    @note
        This method calculates the frame timing information for you based on the elapsed
        time. If you want to specify elapsed times yourself you should call the other 
        version of this method which takes event details as a parameter.
    @returns False if one or more frame listeners elected that the rendering loop should
        be terminated, true otherwise.
    */
    bool _fireFrameEnded();

    /** Gets the number of the next frame to be rendered. 
	@remarks
		Note that this is 'next frame' rather than 'current frame' because
		it indicates the frame number that current changes made to the scene
		will take effect. It is incremented after all rendering commands for
		the current frame have been queued, thus reflecting that if you 
		start performing changes then, you will actually see them in the 
		next frame. 
    */
    unsigned long getNextFrameNumber(void) const { return mNextFrame; }

	/** Clears the history of all event times. 
	@remarks
		OGRE stores a history of the last few event times in order to smooth
		out any inaccuracies and temporary fluctuations. However, if you 
		pause or don't render for a little while this can cause a lurch, so
		if you're resuming rendering after a break, call this method to reset
		the stored times
	*/
	void clearEventTimes(void);

    /** Internal method used for updating all RenderTarget objects (windows, 
        renderable textures etc) which are set to auto-update.
    @remarks
        You don't need to use this method if you're using Ogre's own internal
        rendering loop (Root::startRendering). If you're running your own loop
        you may wish to call it to update all the render targets which are
        set to auto update (RenderTarget::setAutoUpdated). You can also update
        individual RenderTarget instances using their own update() method.
	@returns false if a FrameListener indicated it wishes to exit the render loop
    */
    bool _updateAllRenderTargets(void);

    /** Internal method used for updating all RenderTarget objects (windows, 
        renderable textures etc) which are set to auto-update, with a custom time
		passed to the frameRenderingQueued events.
    @remarks
        You don't need to use this method if you're using Ogre's own internal
        rendering loop (Root::startRendering). If you're running your own loop
        you may wish to call it to update all the render targets which are
        set to auto update (RenderTarget::setAutoUpdated). You can also update
        individual RenderTarget instances using their own update() method.
	@returns false if a FrameListener indicated it wishes to exit the render loop
    */
    bool _updateAllRenderTargets(FrameEvent& evt);

	/** Get the WorkQueue for processing background tasks.
		You are free to add new requests and handlers to this queue to
		process your custom background tasks using the shared thread pool. 
		However, you must remember to assign yourself a new channel through 
		which to process your tasks.
	*/
	WorkQueue* getWorkQueue() const { return mWorkQueue; }

	/** Replace the current work queue with an alternative. 
		You can use this method to replace the internal implementation of
		WorkQueue with  your own, e.g. to externalise the processing of 
		background events. Doing so will delete the existing queue and
		replace it with this one. 
	@param queue The new WorkQueue instance. Root will delete this work queue
		at shutdown, so do not destroy it yourself.
	*/
	void setWorkQueue(WorkQueue* queue);

    /** Initialises the renderer.
    @remarks
        This method can only be called after a renderer has been
        selected with Root::setRenderSystem, and it will initialise
        the selected rendering system ready for use.
    @param
        autoCreateWindow If true, a rendering window will
        automatically be created (saving a call to
        Root::createRenderWindow). The window will be
        created based on the options currently set on the render
        system.
    @returns
        A pointer to the automatically created window, if
        requested, otherwise <b>NULL</b>.
    */
    U2RenderWindow* initialise(bool autoCreateWindow, const U2String& windowTitle = "U2 Render Window",
                                const U2String& customCapabilitiesConfig = U2StringUtil::BLANK);

    /** Returns whether the system is initialised or not. */
    bool isInitialised(void) const { return m_bInitialised; }

    /** Requests active RenderSystem to use custom RenderSystemCapabilities
    @remarks
        This is useful for testing how the RenderSystem would behave on a machine with
        less advanced GPUs. This method MUST be called before creating the first RenderWindow
    */
    void useCustomRenderSystemCapabilities(U2RenderSystemCapabilities* capabilities);

    /** Returns the scene manager currently being used to render a frame.
    @remarks
        This is only intended for internal use; it is only valid during the
        rendering of a frame.
    */
    U2SceneManager* _getCurrentSceneManager(void) const;
    /** Pushes the scene manager currently being used to render.
    @remarks
        This is only intended for internal use.
    */
    void _pushCurrentSceneManager(U2SceneManager* sm);
	/** Pops the scene manager currently being used to render.
	@remarks
	This is only intended for internal use.
	*/
	void _popCurrentSceneManager(U2SceneManager* sm);
	/** Register a new U2SceneManagerFactory, a factory object for creating instances
		of specific SceneManagers. 
	@remarks
		Plugins should call this to register as new U2SceneManager providers.
	*/
	void addSceneManagerFactory(U2SceneManagerFactory* fact);

	/** Unregister a U2SceneManagerFactory.
	*/
	void removeSceneManagerFactory(U2SceneManagerFactory* fact);

	/** Get more information about a given type of U2SceneManager.
	@remarks
		The metadata returned tells you a few things about a given type 
		of U2SceneManager, which can be created using a factory that has been
		registered already. 
	@param typeName The type name of the U2SceneManager you want to enquire on.
		If you don't know the typeName already, you can iterate over the 
		metadata for all types using getMetaDataIterator.
	*/
	const SceneManagerMetaData* getSceneManagerMetaData(const U2String& typeName) const;

	/** Iterate over all types of U2SceneManager available for construction, 
		providing some information about each one.
	*/
	U2SceneManagerEnumerator::MetaDataIterator getSceneManagerMetaDataIterator(void) const;

	/** Create a U2SceneManager instance of a given type.
	@remarks
		You can use this method to create a U2SceneManager instance of a 
		given specific type. You may know this type already, or you may
		have discovered it by looking at the results from getMetaDataIterator.
	@note
		This method throws an exception if the named type is not found.
	@param typeName U2String identifying a unique U2SceneManager type
	@param instanceName Optional name to given the new instance that is
		created. If you leave this blank, an auto name will be assigned.
	*/
	U2SceneManager* createSceneManager(const U2String& typeName, 
		const U2String& instanceName = U2StringUtil::BLANK);

	/** Create a U2SceneManager instance based on scene type support.
	@remarks
		Creates an instance of a U2SceneManager which supports the scene types
		identified in the parameter. If more than one type of U2SceneManager 
		has been registered as handling that combination of scene types, 
		in instance of the last one registered is returned.
	@note This method always succeeds, if a specific scene manager is not
		found, the default implementation is always returned.
	@param typeMask A mask containing one or more SceneType flags
	@param instanceName Optional name to given the new instance that is
		created. If you leave this blank, an auto name will be assigned.
	*/
	U2SceneManager* createSceneManager(SceneTypeMask typeMask, 
		const U2String& instanceName = U2StringUtil::BLANK);

	/** Destroy an instance of a U2SceneManager. */
	void destroySceneManager(U2SceneManager* sm);

	/** Get an existing U2SceneManager instance that has already been created,
		identified by the instance name.
	@param instanceName The name of the instance to retrieve.
	*/
	U2SceneManager* getSceneManager(const U2String& instanceName) const;

	/** Determines if a given U2SceneManager already exists
	@param instanceName The name of the instance to retrieve.
	*/
	bool hasSceneManager(const U2String& instanceName) const;
	/** Get an iterator over all the existing U2SceneManager instances. */
	U2SceneManagerEnumerator::SceneManagerIterator getSceneManagerIterator(void);


protected:
    /** Indicates the type of event to be considered by calculateEventTime(). */
    enum FrameEventTimeType {
        FETT_ANY = 0, 
        FETT_STARTED = 1, 
        FETT_QUEUED = 2, 
        FETT_ENDED = 3, 
        FETT_COUNT = 4
    };

    /// Contains the times of recently fired events
    typedef std::deque<unsigned long>   EventTimesQueue;
    EventTimesQueue                     mEventTimes[FETT_COUNT];


protected:

    /** Method reads a plugins configuration file and instantiates all
        plugins.
    @param
        pluginsfile The file that contains plugins information.
        Defaults to "plugins.cfg".
    */
    void loadPlugins( const U2String& pluginsfile = "plugins.cfg" );
	/** Initialise all loaded plugins - allows plugins to perform actions
		once the renderer is initialised.
	*/
	void initialisePlugins();
	/** Shuts down all loaded plugins - allows things to be tidied up whilst
		all plugins are still loaded.
	*/
	void shutdownPlugins();
	/** Unloads all loaded plugins.
    */
    void unloadPlugins();

    /** Internal method for calculating the average time between recently fired events.
    @param now The current time in ms.
    @param type The type of event to be considered.
    */
    u2real calculateEventTime(unsigned long now, FrameEventTimeType type);

	/** Update a set of event times (note, progressive, only call once for each type per frame) */
	void populateFrameEvent(FrameEventTimeType type, FrameEvent& evtToUpdate);

    // Internal method for one-time tasks after first window creation
    void oneTimePostWindowInit(void);

protected:
    // Singletons
    U2LogManager                        *mLogManager;
    U2DynLibManager                     *mDynLibManager;
    U2ArchiveManager                    *mArchiveManager;
    U2ArchiveFactory                    *mArchiveFactoryFileSystem;
    U2ResourceGroupManager              *mResourceGroupManager;
    U2ResourceBackgroundQueue           *mResourceBackgroundQueue;
    U2RenderSystemCapabilitiesManager   *mRenderSystemCapabilitiesManager;
    typedef std::deque<U2SceneManager*> SceneManagerStack;
    SceneManagerStack                   mSceneManagerStack;
    U2SceneManagerEnumerator            *mSceneManagerEnum;
    ControllerManager                   *mControllerManager;

protected:
	U2String				m_szVersion;
	/// List of plugin DLLs loaded
	PluginLibList			m_pluginLibs;
	/// List of Plugin instances registered
	PluginInstanceList		m_plugins;
	bool					m_bInitialised;

    U2RenderSystemList      mRenderers;
    U2RenderSystem*         mActiveRenderer;
    U2RenderWindow*         mAutoWindow;

    U2Timer                 *mTimer;

    /** Set of registered frame listeners */
    std::set<U2FrameListener*>      mFrameListeners;
    /** Set of frame listeners marked for removal*/
    std::set<U2FrameListener*>      mRemovedFrameListeners;

    bool                    mQueuedEnd;
    u2real                  mFrameSmoothingTime;
    unsigned long           mNextFrame;

    WorkQueue*              mWorkQueue;

    U2String                mConfigFileName;

    /// List of plugin DLLs loaded
    PluginLibList           mPluginLibs;
    /// List of Plugin instances registered
    PluginInstanceList      mPlugins;

    // In case multiple render windows are created, only once are the resources loaded.
    bool                    mFirstTimePostWindowInit;
};

U2EG_NAMESPACE_END

#endif